﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections;
using System.ComponentModel;
using System.Reflection;
using System.Web;
using System.Web.Compilation;
using System.IO;
using System.Collections.Specialized;
using System.Linq.Expressions;
using System.Data;
namespace System.Web.Mvc {

    [global::System.Serializable]
    public class RenderViewControlException : Exception {
        public RenderViewControlException() { }
        public RenderViewControlException(string message) : base(message) { }
        public RenderViewControlException(string message, Exception inner) : base(message, inner) { }
        protected RenderViewControlException(
          System.Runtime.Serialization.SerializationInfo info,
          System.Runtime.Serialization.StreamingContext context)
            : base(info, context) { }
    }

    public static class UserControlExtensions {

        
        /// <summary>
        /// Renders the specified ViewUserControl to a string
        /// </summary>
        /// <param name="virtualPath">The virtual path to the control</param>
        /// <returns>System.String</returns>
        public static string RenderUserControl(this ViewPage viewPage, string virtualPath) {
            return RenderUserControl(viewPage, virtualPath, null);
        }

        /// <summary>
        /// Renders the specified ViewUserControl to a string
        /// </summary>
        /// <param name="virtualPath">The virtual path to the control</param>
        /// <param name="controlData">The data to send to the control as ViewData</param>
        /// <returns>System.String</returns>
        public static string RenderUserControl(this ViewPage viewPage, string virtualPath, object controlData) {
            virtualPath = VirtualPathUtility.Combine(viewPage.AppRelativeTemplateSourceDirectory, virtualPath);
            return viewPage.Html.RenderUserControl(virtualPath, controlData, null);
        }

        /// <summary>
        /// Renders the specified ViewUserControl to a string
        /// </summary>
        /// <param name="virtualPath">The virtual path to the control</param>
        /// <param name="controlData">The data to send to the control as ViewData</param>
        /// <param name="propertySettings">Property settings for the control. Use Anonymous Typing for this: new{Name="MVC"}</param>
        /// <returns>System.String</returns>
        public static string RenderUserControl(this ViewPage viewPage, string virtualPath, object controlData, object propertySettings) {
            virtualPath = VirtualPathUtility.Combine(viewPage.AppRelativeTemplateSourceDirectory, virtualPath);
            return viewPage.Html.RenderUserControl(virtualPath, controlData, propertySettings);
        }

        /// <summary>
        /// Instances a ViewUserControl located at the supplied virtual path
        /// </summary>
        /// <param name="virtualPath">the virtual path to the control</param>
        /// <returns>ViewUserControl</returns>
        static ViewUserControl InstanceControl(string virtualPath) {

            Type ctrlType;
            ViewUserControl instance = null;

            try {
                //check for the control, using the path as the key

                ctrlType = BuildManager.GetCompiledType(virtualPath);

                ConstructorInfo ctor = ctrlType.GetConstructor(new Type[] { });
                instance = (ViewUserControl)ctor.Invoke(new object[] { });

            } catch (Exception x) {
                throw new RenderViewControlException("Unable to instance the ViewUserControl: " + x.Message, x);

            }
            return instance;
        }

        /// <summary>
        /// Creates a ViewUserPage class to hold the ViewUserControl for rendering
        /// </summary>
        /// <param name="instance">The instance of the ViewUserControl</param>
        /// <returns>HtmlExtensionUtility.CustomPage</returns>
        static HtmlExtensionUtility.CustomPage GetPageForControl(ViewUserControl instance) {
            HtmlExtensionUtility.CustomPage dummyPage = new HtmlExtensionUtility.CustomPage();
            dummyPage.Controls.Add(instance);
            return dummyPage;
        }

        /// <summary>
        /// Passes the UrlHelper and HtmlHelper instances to the ViewUserControl
        /// </summary>
        /// <param name="dummyPage">The page which contains the contexts</param>
        /// <param name="instance">The ViewUserControl instance</param>
        static void PassContextToUserControl(HtmlHelper helperInstance, HtmlExtensionUtility.CustomPage dummyPage, ViewUserControl instance) {
            try {
                Type ctrlType = instance.GetType();

                //set the Url and Html
                MethodInfo mSet = dummyPage.GetType().GetMethod("set_Url", BindingFlags.NonPublic | BindingFlags.Instance);
                mSet.Invoke(dummyPage, new object[] { new UrlHelper(helperInstance.ViewContext) });

                mSet = dummyPage.GetType().GetMethod("set_Html", BindingFlags.NonPublic | BindingFlags.Instance);
                mSet.Invoke(dummyPage, new object[] { new HtmlHelper(helperInstance.ViewContext) });

            } catch(Exception x) {
                throw new RenderViewControlException("Unable to pass the context to the ViewUserControl: " + x.Message+". Make sure you're calling this control from a Type ViewUserPage", x);
            }
        }

        /// <summary>
        /// Passes the data to the control
        /// </summary>
        /// <param name="helperInstance">The instance of the HTMLHelper</param>
        /// <param name="instance">The ViewUserControl instance</param>
        /// <param name="controlData">The data to pass to the control</param>
        static void SetUserControlData(HtmlHelper helperInstance, ViewUserControl instance, object controlData) {
            Type ctrlType = instance.GetType();

            try {
                //HACK - this will be changing after CTP
                MethodInfo mSet = ctrlType.GetMethod("SetViewData", BindingFlags.NonPublic | BindingFlags.Instance);
                mSet.Invoke(instance, new object[] { controlData });
            } catch (Exception x) {
                throw new RenderViewControlException("Error setting ViewData for the ViewUserControl: " + x.Message, x);

            }

        }

        /// <summary>
        /// Sets any properties on the control
        /// </summary>
        /// <param name="instance">The ViewUserControl instance</param>
        /// <param name="propertySettings"></param>
        static void SetUserControlProperties(ViewUserControl instance, object propertySettings) {
            //property setter bits
            try {
                if (propertySettings != null) {
                    Hashtable props = HtmlExtensionUtility.GetPropertyHash(propertySettings);
                    HtmlExtensionUtility.SetPropsFromHash(instance, props);
                }
            } catch (Exception x) {
                throw new RenderViewControlException("Error setting properties for the ViewUserControl: " + x.Message, x);

            }

        }




        /// <summary>
        /// Renders the specified ViewUserControl to a string
        /// </summary>
        /// <param name="virtualPath">The virtual path to the control</param>
        /// <returns>System.String</returns>
        public static string RenderUserControl(this HtmlHelper helper, string virtualPath) {
            return RenderUserControl(helper, virtualPath, null, null);
        }

        /// <summary>
        /// Renders the specified ViewUserControl to a string
        /// </summary>
        /// <param name="virtualPath">The virtual path to the control</param>
        /// <param name="controlData">The data to send to the control as ViewData</param>
        /// <returns>System.String</returns>
        public static string RenderUserControl(this HtmlHelper helper, string virtualPath, object controlData) {
            return RenderUserControl(helper, virtualPath, controlData, null);
        }

        /// <summary>
        /// Renders the specified ViewUserControl to a string
        /// </summary>
        /// <param name="virtualPath">The virtual path to the control</param>
        /// <param name="controlData">The data to send to the control as ViewData</param>
        /// <param name="propertySettings">Property settings for the control. Use Anonymous Typing for this: new{Name="MVC"}</param>
        /// <returns>System.String</returns>
        public static string RenderUserControl(this HtmlHelper helper, string virtualPath, object controlData, object propertySettings) {

            //instance control
            ViewUserControl instance = InstanceControl(virtualPath);

            HtmlExtensionUtility.CustomPage dummyPage = GetPageForControl(instance);

            //pass it the default context from the helper
            PassContextToUserControl(helper, dummyPage, instance);

            //set the properties
            SetUserControlProperties(instance, propertySettings);

            if (controlData == null) {
                SetUserControlData(helper, instance, helper.ViewContext.ViewData);
            } else {

                SetUserControlData(helper, instance, controlData);

            }

            //Render it
            string result = HtmlExtensionUtility.RenderPage(dummyPage);

            return result;

        }



    }
}
